﻿using System.Text; 

namespace Game
{
    public static class ConsoleIO
    {
        private const string k_EndGameChar = "Q";
        private const string k_AIPlayerName = "Sasson HaGaon";
        private const char k_LineToken = '=';
        private const char k_PipeToken = '|';
        private const char k_FirstColumnLetter = 'A';
        private const char k_BlankSpace = ' ';
        private const int k_SpaceOnLeft = 2;
        private const int k_SpaceOnRight = 3;

        public static void ShowLogoScreen()
        {
            string Logo =
@"                                                         Brace for Epic...
::::    ::::: :::::::::: ::::    :::::  ::::::::  :::::::::  :::   ::: ::: 
+:+:+: :+:+:+ :+:        +:+:+: :+:+:+ :+:    :+: :+:    :+: :+:   :+: :+: 
+:+ +:+:+ +:+ +:+        +:+ +:+:+ +:+ +:+    +:+ +:+    +:+  +:+ +:+  +:+ 
+#+  +:+  +#+ +#++:++#   +#+  +:+  +#+ +#+    +:+ +#++:++#:    +#++:   +#+ 
+#+       +#+ +#+        +#+       +#+ +#+    +#+ +#+    +#+    +#+    +#+ 
#+#       #+# #+#        #+#       #+# #+#    #+# #+#    #+#    #+#        
###       ### ########## ###       ###  ########  ###    ###    ###    ### 
";
            System.Console.ForegroundColor = System.ConsoleColor.DarkCyan;
            System.Console.WriteLine(Logo);
            System.Console.ResetColor();
        }

        public static Engine.GameSettings GetSettingsFromUser()
        {
            Engine.GameSettings Settings = new Engine.GameSettings();
            
            System.Console.WriteLine("*** Game Settings ***");
            GetPlayersForSettings(ref Settings);
            GetDimensionsForSettings(ref Settings);
                        
            string msgToUser = string.Format(
                "{0}{1} Vs {2} On a {3}x{4} board.{0}(Press any key to continue...)", 
                System.Environment.NewLine,
                Settings.Player1Name,
                Settings.Player2Name,
                Settings.Rows.ToString(),
                Settings.Cols.ToString());
            System.Console.Write(msgToUser);

            const bool v_InterceptKey = true;
            System.Console.ReadKey(v_InterceptKey);

            return Settings;
        }

        public static void GetPlayersForSettings(ref Engine.GameSettings io_GameSettings)
        {
            const int k_playerNumber1 = 1;
            const int k_playerNumber2 = 2;

            io_GameSettings.Player1Name = getPlayerNameFromUser(k_playerNumber1);
            io_GameSettings.IsSinglePlayer = getIsSinglePlayerFromUser();
            io_GameSettings.Player2Name = io_GameSettings.IsSinglePlayer ? k_AIPlayerName : getPlayerNameFromUser(k_playerNumber2);
        }

        public static void GetDimensionsForSettings(ref Engine.GameSettings io_GameSettings)
        {
            int[] BoardDimensions = new int[2];

            BoardDimensions = getBoardDimensionsFromUser();
            io_GameSettings.Rows = BoardDimensions[0];
            io_GameSettings.Cols = BoardDimensions[1];
        }

        public static void DrawBoard(Game.Engine i_Engine)
        {
            StringBuilder boardStr = new StringBuilder();
            int numOfLineTokens = (i_Engine.Board.Cols * 4) + 1;
            string lineSeparatorStr = buildLineSeperator(numOfLineTokens);
            string FirstBoardLine = buildLetterLineOfBoard(i_Engine.Board.Cols);
            string spacePipeSpace = buildSpacerToken();
            Game.Board.Coordinate currentCordsOnBoard = Game.Board.Coordinate.MakeEmpty();

            boardStr.AppendLine(FirstBoardLine).AppendLine(lineSeparatorStr);  
                                   
            for (int i = 0; i < i_Engine.Board.Rows; i++)
            {
                boardStr.Append((i + 1).ToString());  // #
                for (int j = 0; j < i_Engine.Board.Cols; j++)
                {
                    boardStr.Append(spacePipeSpace); // #_|_
                    currentCordsOnBoard.i = i;
                    currentCordsOnBoard.j = j;
                    if (i_Engine.Board.IsCardVisible(currentCordsOnBoard))
                    {
                        boardStr.Append(i_Engine.Board.GetCard(currentCordsOnBoard).Symbol); // #..._|_$
                    }
                    else
                    {
                        boardStr.Append(k_BlankSpace); // #..._|__
                    }
                }

                boardStr.AppendLine(spacePipeSpace).AppendLine(lineSeparatorStr); // #_|_?_|_?_|_\n   =======
            }

            clearAndPrintLogoAndStatusLine(i_Engine.Player1, i_Engine.Player2);
            System.Console.Write(boardStr.ToString());
        }

        public static void PrintScoreStatusLine(Game.Player i_Player1, Game.Player i_Player2)    
        {
            string msgToUser = string.Format(
                "{0}  :  {1}   VS   {2}  :  {3}{4}{4}",
                i_Player1.Name,
                i_Player1.Score,
                i_Player2.Name,
                i_Player2.Score,
                System.Environment.NewLine);           
            System.Console.Write(msgToUser);
        }

        public static Engine.eInputType GetCommandFromPlayer(Game.Player i_CurrentPlayer, Game.Board i_Board, out Game.Board.Coordinate o_Move)
        {
            System.Console.WriteLine(i_CurrentPlayer.Name + " what is your move?" + System.Environment.NewLine + "('Q' to quit the game)");
            string inputStr = System.Console.ReadLine();

            Engine.eInputType inputType = Engine.eInputType.Invalid;
            do
            {
                if (Board.Coordinate.TryParse(inputStr, i_Board, out o_Move))
                {
                    if (i_Board.IsLegalMove(o_Move))
                    {
                        inputType = Engine.eInputType.Move;
                    }
                    else
                    {
                        System.Console.WriteLine("Invalid Move, choose a card which is not visible." + System.Environment.NewLine);
                        inputStr = System.Console.ReadLine();
                    }
                }
                else if (inputStr.ToUpper() == k_EndGameChar)
                {
                    inputType = Engine.eInputType.Quit;
                }
                else 
                {
                    System.Console.WriteLine("Invalid input (try something like '1A' or 'Q')" + System.Environment.NewLine);
                    inputStr = System.Console.ReadLine();
                }
            } 
            while (inputType == Engine.eInputType.Invalid);

            return inputType;
        }

        public static void EndOfTurn(bool i_Success, Game.Player i_player)
        {
            const bool v_InterceptKey = true;
            string msgToUser = i_Success ? i_player.Name + ", very good! Another turn for you." : "Too bad... turn moves to " + i_player.Name;
            
            msgToUser += System.Environment.NewLine + "(Press any key to continue...)";
            
            System.Console.WriteLine(msgToUser);
            System.Console.ReadKey(v_InterceptKey);
        }

        public static void EndOfMatch(Player[] i_Players)
        {
            clearAndPrintLogoAndStatusLine(i_Players[0], i_Players[1]);

            if (i_Players[0].Score == i_Players[1].Score)
            {
                System.Console.WriteLine("It's a tie!");
            }
            else
            {
                Player winner = (i_Players[0].Score > i_Players[1].Score) ? i_Players[0] : i_Players[1];
                System.Console.WriteLine(winner.Name + " Wins with " + winner.Score.ToString() + " points!");
            }
        }

        public static bool AskForAnotherMatch()
        {
            System.Console.Write("Would you like to play again?" + System.Environment.NewLine + "(y - yes, n - no)");
            string inputFromUser = System.Console.ReadLine();

            while ((inputFromUser.ToLower() != "y") && (inputFromUser.ToLower() != "n"))
            {
                System.Console.Write("Please enter 'y' or 'n'" + System.Environment.NewLine);
                inputFromUser = System.Console.ReadLine();
            }

            bool anotherMatch;
            anotherMatch = (inputFromUser.ToLower() == "y") ? true : false;

            return anotherMatch;
        }
        
        private static bool getIsSinglePlayerFromUser()
        {
            int numOfPlayers;
            string inputFromUser;

            string msgToUser = "How many players are going to play?" + System.Environment.NewLine + "'1' - Player Vs " + k_AIPlayerName + System.Environment.NewLine + "'2' - 2 players in Hotseat";
            System.Console.WriteLine(msgToUser);
            inputFromUser = System.Console.ReadLine();
            
            while (!int.TryParse(inputFromUser, out numOfPlayers) || ((numOfPlayers != 1) && (numOfPlayers != 2)))
            {
                System.Console.WriteLine("Please enter '1' or '2'.");
                inputFromUser = System.Console.ReadLine();
            } 

            const int k_SinglePlayer = 1;

            return numOfPlayers == k_SinglePlayer;
        }

        private static string getPlayerNameFromUser(int i_PlayerNumber)
        {
            string inputFromUser;
            string msgToUser = string.Format("Please enter Player {0} name:{1}", i_PlayerNumber.ToString(), System.Environment.NewLine);
           
            System.Console.Write(msgToUser);
            do
            {
                inputFromUser = System.Console.ReadLine();
                inputFromUser = inputFromUser.Trim();
            } 
            while (inputFromUser == string.Empty);

            return inputFromUser;
        }

        private static int[] getBoardDimensionsFromUser()
        {
            int[] boardDimensions = new int[2];           

            System.Console.WriteLine("Please Enter board size: (minimum 4x4, maximum 6x6)");
            
            boardDimensions[0] = getSizeOfDimFromUser("Rows");
            boardDimensions[1] = getSizeOfDimFromUser("Columns");
            
            while (!Game.Board.IsGoodDimensions(boardDimensions[0], boardDimensions[1]))
            {
                System.Console.WriteLine("Dimensions must be between 4x4, 6x6 and with even number of squares total.");
                boardDimensions[0] = getSizeOfDimFromUser("Rows");
                boardDimensions[1] = getSizeOfDimFromUser("Columns");
            } 

            return boardDimensions;
        }

        private static int getSizeOfDimFromUser(string i_CurrentDimensionStr)
        {
            int dimFromUser;
            string inputFromUser = string.Empty;
            string msgToUser = i_CurrentDimensionStr + ": ";

            System.Console.Write(msgToUser);
            inputFromUser = System.Console.ReadLine();
            while (!int.TryParse(inputFromUser, out dimFromUser))
            {
                System.Console.WriteLine("Please enter a proper number.");
                inputFromUser = System.Console.ReadLine();
            } 

            return dimFromUser;
        }

        private static string buildLineSeperator(int i_NumOfLineTokens)
        {
            StringBuilder lineSeparatorStr = new StringBuilder();

            lineSeparatorStr.Append(k_BlankSpace, k_SpaceOnLeft);
            lineSeparatorStr.Append(k_LineToken, i_NumOfLineTokens);

            return lineSeparatorStr.ToString();
        }

        private static string buildLetterLineOfBoard(int i_NumOfBoardCols)
        {
            StringBuilder firstBoardLine = new StringBuilder();
            int SpacesNeededOnLeft = k_SpaceOnLeft * 2;

            firstBoardLine.Append(k_BlankSpace, SpacesNeededOnLeft); 
    
            for (int i = 0; i < i_NumOfBoardCols; i++)                                  
            {
                char letterAddToLine = (char)((int)k_FirstColumnLetter + i);  
             
                firstBoardLine.Append(letterAddToLine);    
                firstBoardLine.Append(k_BlankSpace, k_SpaceOnRight); 
            }

            return firstBoardLine.ToString();
        }

        private static string buildSpacerToken()
        {
            StringBuilder spacePipeSpace = new StringBuilder();

            spacePipeSpace.AppendFormat("{0}{1}{0}", k_BlankSpace, k_PipeToken); 

            return spacePipeSpace.ToString();
        }

        private static void clearAndPrintLogoAndStatusLine(Game.Player i_player1, Game.Player i_player2)
        {
            Ex02.ConsoleUtils.Screen.Clear();
            ShowLogoScreen();
            PrintScoreStatusLine(i_player1, i_player2);
        }
    }
}
